// this is the entry point for the physics engine.
// this class takes care of managing the program's
// objects and updating them each time it is called
// by the main program loop.

#pragma once

#include "core.h"
#include "collide_fine.h"
#include "particle.h"
#include "rigidbody.h"
#include "fgen.h"
#include "contacts.h"
#include <vector>
#include "../ParticleEffect.h"

namespace physics {

	class PhysicsEngine
	{
	public:
		// for simplicity of coding
		typedef std::vector<CollisionPrimitive*> Objects;
		typedef std::vector<ContactGenerator*> ContactGenerators;
		typedef std::vector<ParticleEffect*> ParticleEffects;

	protected:
		// keeps track of particles in the system
		ParticleEffects particleEffects;

		// keeps track of the program's objects and
		// other classes that inherit from them
		Objects objects;

		// keeps track of whether the user supplied
		// a value for the iterations limit. If not, we
		// just use 2x the number of contacts in the first
		// round.
		bool calculateIterations;

		// the force generators for the objects
		ForceRegistry registry;

		// global forces (e.g. gravity, explosions etc.)
		GlobalForceRegistry globalRegistry;

		// the contact resolver
		ContactResolver resolver;

		// the contact generators
		ContactGenerators contactGenerators;

		// the list we use to keep the contacts
		Contact *contacts;

		// the max number of contacts allowed (size
		// of the array)
		unsigned maxContacts;

		// CollisionData object which stores relevant
		// collision information
		CollisionData cData;

	public:
		// creates a new particle world, which simulates
		// a world of particle objects that behave under
		// newtonian laws of physics.
		PhysicsEngine(unsigned maxContacts = 0, unsigned iterations = 0);

		// deletes the engine
		~PhysicsEngine();

		// generates the contacts
		void generateContacts();

		// integrates all Particles
		void integrate(real dt);
		
		// process physics
		void runPhysics(real dt);

		// ends the frame for the next iteration
		void endFrame();

		// called each time through the main loop, to update
		// all of the program's objects
		void update(real dt);

		// gets the list of particles
		Objects& getObjects();

		// gets the list of contact generators
		ContactGenerators& getContactGenerators();

		// gets the force registry
		ForceRegistry& getForceRegistry();

		// gets the global force registry
		GlobalForceRegistry& getGlobalForceRegistry();

		// adds an object to the engine
		void AddObject(CollisionPrimitive* object);

		// removes an object from the game
		void RemoveObject(RigidBody* object);

		// removes all objects from the game
		void RemoveAllObjects(void);

		// checks if this objects has a collision to resolve in this iteration
		bool HasCollided(RigidBody* object);

		// adds a particle effect to the engine
		void AddParticleEffect(ParticleEffect* particleEffect);

		// removes a particle effect from the engine
		void RemoveParticleEffect(ParticleEffect* particleEffect);

		// removes all particlesEffects
		void RemoveAllParticleEffects(void);
	};

	// a contact generator that inherits from the ABC
	// that generates contacts with the ground
	class GroundContacts : public physics::ContactGenerator
	{
	private:
		physics::PhysicsEngine::Objects *particles;

	public:
		void init(physics::PhysicsEngine::Objects *particles);

		virtual unsigned addContact(physics::Contact *contact, unsigned limit) const;

	};

}